package com.cskaoyan.mall.promo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cskaoyan.mall.common.constant.RedisConst;
import com.cskaoyan.mall.mq.constant.MqTopicConst;
import com.cskaoyan.mall.order.dto.OrderDetailDTO;
import com.cskaoyan.mall.order.dto.OrderTradeDTO;
import com.cskaoyan.mall.order.query.OrderInfoParam;
import com.cskaoyan.mall.promo.api.dto.SeckillGoodsDTO;
import com.cskaoyan.mall.promo.client.OrderApiClient;
import com.cskaoyan.mall.promo.client.UserApiClient;
import com.cskaoyan.mall.promo.constant.LocalCacheEnum;
import com.cskaoyan.mall.promo.constant.SeckillGoodsStatus;
import com.cskaoyan.mall.promo.converter.SeckillGoodsConverter;
import com.cskaoyan.mall.promo.mapper.SeckillGoodsMapper;
import com.cskaoyan.mall.promo.model.SeckillGoods;
import com.cskaoyan.mall.promo.mq.PromoTransactionProducer;
import com.cskaoyan.mall.promo.redis.RedisStockOper;
import com.cskaoyan.mall.promo.service.PromoService;
import com.cskaoyan.mall.promo.util.LocalCacheHelper;
import com.cskaoyan.mall.user.dto.UserAddressDTO;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class PromoServiceImpl implements PromoService {
    @Autowired
    SeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    SeckillGoodsConverter seckillGoodsConverter;
    @Autowired
    UserApiClient userApiClient;
    @Autowired
    OrderApiClient orderApiClient;
    @Autowired
    PromoTransactionProducer promoTransactionProducer;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    RedisStockOper redisStockOper;
    @Override
    public void importIntoRedis() {
        //这个地方我们要把数据存入本地内存中
        //先把需要的数据查出来
        //查找日期是今天的列表，主要是判断日期。是否有库存，是否被批准了
        List<SeckillGoods> seckillGoods = getTodaySeckillList();


        //然后根据键值对存列表，存一个map
        RMap<Long, SeckillGoods> seckillListMap = redissonClient.getMap(RedisConst.PROMO_SECKILL_GOODS);


        //这个地方把库存也存一下，库存也用map
        RMap<Long, Integer> stockMap = redissonClient.getMap(RedisConst.PROMO_SECKILL_GOODS_STOCK, new StringCodec());

        //先把之前的数据清除
        seckillListMap.clear();
        stockMap.clear();
        LocalCacheHelper.removeAll();
        for (SeckillGoods seckillGood : seckillGoods) {
            LocalCacheHelper.put(String.valueOf(seckillGood.getSkuId()), LocalCacheEnum.HAS_STOCK.getCode());
            //把商品存入redis
            seckillListMap.put(seckillGood.getSkuId(),seckillGood);
            stockMap.put(seckillGood.getSkuId(), seckillGood.getStockCount());
        }
    }

    private List<SeckillGoods> getTodaySeckillList() {
        QueryWrapper<SeckillGoods> seckillWrapper=new QueryWrapper<>();
        seckillWrapper.eq("status", SeckillGoodsStatus.CHECKED_PASS.name());
        seckillWrapper.gt("stock_count",0);

        Date current=new Date();
        //然后做一个转换，怎么做呢
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = simpleDateFormat.format(current);
        seckillWrapper.eq("DATE_FORMAT(start_time,'%Y-%m-%d')", format);

        List<SeckillGoods> seckillGoods = seckillGoodsMapper.selectList(seckillWrapper);
        return seckillGoods;
    }

    @Override
    public SeckillGoodsDTO getSeckillGoodsDTO(Long skuId) {
        RMap<Long, SeckillGoods> seckillListMap = redissonClient.getMap(RedisConst.PROMO_SECKILL_GOODS);
        SeckillGoods seckillGoods = seckillListMap.get(skuId);
        SeckillGoodsDTO seckillGoodsDTO = seckillGoodsConverter.convertSeckillGoodsToDTO(seckillGoods);
        return seckillGoodsDTO;
    }

    @Override
    public boolean checkOrder(Long skuId, String userId) {
        //查看是否有对应的用户信息
        RMap<String, Long> submitMap = redissonClient.getMap(RedisConst.PROMO_SUBMIT_ORDER);
        String key=userId+skuId;
        Long msgId = submitMap.get(key);
        return msgId!=null;
    }

    @Override
    public void clearRedisCache() {
        //促销结束，把相关内容写入和清除
        RMap<String, String> stockMap = redissonClient.getMap(RedisConst.PROMO_SECKILL_GOODS_STOCK, new StringCodec());
        List<SeckillGoods> todaySeckillList = getTodaySeckillList();
        for (SeckillGoods seckillGoods : todaySeckillList) {
            seckillGoods.setStockCount(Integer.valueOf(stockMap.get(String.valueOf(seckillGoods.getSkuId()))));
            seckillGoodsMapper.updateById(seckillGoods);
        }
        stockMap.clear();
        LocalCacheHelper.removeAll();
        redissonClient.getKeys().deleteByPattern("promo:*");


    }

    @Override
    public OrderTradeDTO getTradeData(String userId, Long skuId) {
        //组装orderInfo
        //这个地方应该是要调用不同的服务
        //TODO 这个地方要改成redis，，但是懒惰，，很可能这个todo在你看到的时候还是个todo，，，
        List<UserAddressDTO> userAddressListByUserId = userApiClient.findUserAddressListByUserId(userId);
        SeckillGoodsDTO seckillGoodsDTO = this.getSeckillGoodsDTO(skuId);
        OrderDetailDTO orderDetailDTO = seckillGoodsConverter.secondKillGoodsDTOToOrderDetailDTO(seckillGoodsDTO, 1);
        OrderTradeDTO orderTradeDTO=new OrderTradeDTO();
        orderTradeDTO.setUserAddressList(userAddressListByUserId);
        orderTradeDTO.setDetailArrayList(Arrays.asList(orderDetailDTO));
        orderTradeDTO.setTotalNum(1);
        orderTradeDTO.setTotalAmount(orderDetailDTO.getOrderPrice());
        return orderTradeDTO;
    }

    @Override
    public boolean submitOrder(OrderInfoParam orderInfo) throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
        boolean result = promoTransactionProducer.sendMsg(MqTopicConst.SECKILL_GOODS_QUEUE_TOPIC, orderInfo);
        //如果结果是false，回滚redis内的数据
        return result;
    }

    @Override
    public boolean reduceStock(OrderInfoParam orderInfo) {
        //这个地方主要维护扣减库存的逻辑
        try {
            Long skuId = orderInfo.getOrderDetailList().get(0).getSkuId();
            Integer skuNum = orderInfo.getOrderDetailList().get(0).getSkuNum();

            //这个地方主要要做的是调用lua脚本删改库存
            Long i = redisStockOper.decrRedisStock(skuId, skuNum);

            if (i<0){
                return false;
            }
            return true;
        }catch (Exception e){
            return false;
        }
    }

    @Override
    public void submitOrderInTransaction(OrderInfoParam orderInfo) {
        //这个不需要写
    }

    @Override
    public List<SeckillGoodsDTO> findAll() {
        //查找所有数据
        RMap<Long, SeckillGoods> seckillListMap = redissonClient.getMap(RedisConst.PROMO_SECKILL_GOODS);
        //这个地方把库存也存一下，库存也用map
        RMap<String, String> stockMap = redissonClient.getMap(RedisConst.PROMO_SECKILL_GOODS_STOCK, new StringCodec());
        List<SeckillGoods> seckillGoods = seckillListMap.values().stream().map((item)->{
            Long skuId = item.getSkuId();
            String stock = stockMap.get(String.valueOf(skuId));
            item.setStockCount(Integer.valueOf(stock));
            return item;
        }).collect(Collectors.toList());
        return seckillGoodsConverter.convertSeckillGoodsList(seckillGoods);
    }
}
